/*
srkrt_method.c
Ce fichier contient le corp des diverses fonctions utilisé pour manipuler la structure Surakarta.
En principe, il faudrait toujours modifier les instances de Surakarta avec ces fonctions.
*/

#include "srkrt_method.h"
#include <stdlib.h>
#include <stdio.h>

Surakarta srkrt_create(int turn) {
	
	Surakarta s ; /* Cette structure est celle qui sera renvoyée. */
	int i,j ; /* Ces deux index vont servir à parcourir la grille */
	
	/* Placement intial des pions. */
	for(i=0 ; i<2 ; i++) {
		for(j=0 ; j<6 ; j++) {
			s.board[i][j]=PLAYER_2 ;
		}
	}
	for(i=5 ; i>3 ; i--) {
		for(j=0 ; j<6 ; j++) {
			s.board[i][j]=PLAYER_1 ;
		}
	}
	
	for(i=2 ; i<4 ; i++) {
		for(j=0 ; j<6 ; j++) {
			s.board[i][j]=VOID ;
		}
	}
	
	/* Indique le joueur qui commence */
	s.toPlay=turn ;
	
	/* Initialisation du nombre des pions restants. */
	s.player1Left=12 ;
	s.player2Left=12 ;
	
	/* Initialisation de l'historique des coups.*/
	s.history = stack_create() ;
	
	return s ;
}


void srkrt_play(Surakarta* game, Move action) {
	
	/* Déplacement du pion demandé.*/
	game->board[action.xf][action.yf]=game->toPlay ;
	game->board[action.xi][action.yi]=VOID ;
	
	/* si il y a une prise, on diminue le nombre de pions restant */
	if ( action.capture ) {
		if ( game->toPlay == PLAYER_1 ) {
			(game->player2Left)--;
		} else {
			(game->player1Left)--;
		}
	}
	
	/* Changement du joueur dont c'est le tour, et ajout du coup joué dans l'historique. */
	(game->toPlay)*=-1 ;
	stack_add(&(game->history),action) ;
}

Move move_create(int xi,int yi,int xf,int yf,bool c) {
	
	Move m ;
	m.xi=xi ;
	m.yi=yi ;
	m.xf=xf ;
	m.yf=yf ;
	m.capture=c ;
	
	return m ;
	
}

void srkrt_take_back(Surakarta* game) {

	if(!stack_is_empty(&(game->history))) { /* Si aucun coup n'a encore été joué, rien de ne passe. */
		
		/* On commence par dépiler l'historique. */
		Move m = stack_head(&(game->history)) ;
		stack_remove(&(game->history)) ;
		
		/* Ensuite, on déplace le pion en sens inverse. */
		game->board[m.xi][m.yi] = game->toPlay*-1 ;
		/* Sans oublier de remettre un pion en jeu si le coup annullé était une capture. */
		game->board[m.xf][m.yf] = game->toPlay*m.capture ;
		if(m.capture) {
			if(game->toPlay==PLAYER_1)
				(game->player1Left)++ ;
			else
				(game->player2Left)++ ;
		}
		/* Enfin, on met à jour le joueur dont c'est le tour. */
		(game->toPlay)*=-1 ;
	} else {
		fprintf(stderr,"No move to take back.\n") ;
	}
}

void srkrt_allowed_moves(Surakarta* game, List* list) {
	
	Move move;
	bool captureIsPossible;
	int sens;
	int i, j;
	int k, l;
	
	for ( i = 0 ; i < 6 ; i++ ) {
		for ( j = 0 ; j < 6 ; j++ ) {
			move = move_create(i, j, 0, 0, false);
			/* si le pion qui est sur cette case appartient au joueur dont c'est le tour de jeu */
			if ( game->board[i][j] == game->toPlay ) {
				/* ajout à la liste des déplacements autour du pion ( pas de prises )*/
				for ( k = i-1 ; k < i+2 ; k++  ) {
					for ( l = j-1 ; l < j+2 ; l++ ) {
						if ( !((k == i) && (l == j)) ) {
							/* si on ne sort pas du plateau */
							if ( (k >= 0) && (k < 6) && (l >= 0 ) && (l < 6 ) ) {
								/* si la case est libre */
								if ( game->board[k][l] == VOID ) {
									move = move_create(i, j, k, l, false);
									/* on l'ajoute à la liste */
									srkrt_list_add(list, move);
								}
							}
						}
					}
				}
				
				/* ajout à la liste des positions suites à des prises */
				for ( sens = UP ; sens <= RIGHT ; sens++ ) {
					captureIsPossible = false;
					srkrt_launch_pawn(game, &move, sens, &captureIsPossible);
					
					if ( captureIsPossible ) {
						/* on ajoute a la liste */
						move.capture = true;
						srkrt_list_add(list, move);
					}
				}
			}
		}
	}
}

void srkrt_launch_pawn(Surakarta* game, Move* move, int dir, bool *captureIsPossible) {
	
	int loopNumber = 0;
	int sens = dir;
	
	/* initialisation du point d'arrivé */
	move->xf = move->xi;
	move->yf = move->yi;
	
	/* tant qu'on n'a pas trouvé de point d'arrét et qu'on a pris au plus 3 boucles on avance d'une case dans la direction sens */
	do {
		/* on avance d'une case */
		srkrt_one_square_move(&sens, move, &loopNumber);
	}
	while ( ( (game->board[move->xf][move->yf] == VOID) || ( (loopNumber == 1) && (move->xi == move->xf) 
		&& (move->yi == move->yf))) && (loopNumber < 5) );
	
	/* si on a pris au moins une boucle et qu'un pion adverse se trouve sur la case d'arrivée, la capture est possible */
	if (  loopNumber && (game->board[move->xf][move->yf] == (-1)*game->toPlay) ) {
		*captureIsPossible = true;
	}
}

/* change la position si on prend une boucle */
void srkrt_loop_arrived(Move *move, int* sens) {
	
	int tmp;
	
	/* si on est dans la boucle supérieure gauche ou inférieure droite */
	if ( (move->xf>=3) == (move->yf>=3) ) {
		/* changement du sens */
		if ( move->xf == 0 ) {
			*sens = RIGHT;
		} else if ( move->xf == 5 ) {
			*sens = LEFT;
		} else if ( move->yf == 0 ) {
			*sens = DOWN;
		} else {
			*sens = UP;
		}
		
		/* on inverse les coordonnées */
		tmp = move->xf;
		move->xf = move->yf;
		move->yf = tmp;
		
	} else { /* si on est dans la boucle supérieure droite ou inférieure gauche */		
		/* changement du sens */
		if ( move->xf == 0 ) {
			*sens = LEFT;
		} else if ( move->xf == 5 ) {
			*sens = RIGHT;
		} else if ( move->yf == 0 ) {
			*sens = UP;
		} else {
			*sens = DOWN;
		}

		/* calcul des nouvelles coordonnées */
		tmp = move->xf;
		move->xf = 5 - move->yf;
		move->yf = 5 - tmp;
	}
}

/* one_square_move calcule la prochaine case a prendre */
void srkrt_one_square_move(int* sens, Move *move, int *loopNumber) {
	
	switch (*sens) {
		case LEFT:
			/* si on se trouve dans un angle : y = 0 */
			if ( (move->xf == 0) || (move->xf == 5) ) {
				/* on positionne xf et yf à xi et yi pour sortir de la boucle */
				move->xf = move->xi;
				move->yf = move->yi;
			} else if ( move->yf == 0) { /* si on peut entrer dans une boucle */
				srkrt_loop_arrived(move, sens);
				(*loopNumber)++;
			} else { /* sinon on se déplace d'une case vers la gauche */
				(move->yf)--;
			}
			break;
		case RIGHT:
			/* si on se trouve dans un angle : y = 5 */
			if ( (move->xf == 0) || (move->xf == 5) ) {
				/* on positionne xf et yf à xi et yi pour sortir de la boucle */
				move->xf = move->xi;
				move->yf = move->yi;
			} else if ( move->yf == 5 ) { /* si on peut entrer dans une boucle */
				srkrt_loop_arrived(move, sens);
				(*loopNumber)++;
			} else { /* sinon on se déplace d'une case vers la droite */	
				(move->yf)++;
			}
			break;
		case UP:
			/* si on se trouve dans un angle : x = 0 */
			if ( (move->yf == 0) || (move->yf == 5) ) {
				/* on positionne xf et yf à xi et yi pour sortir de la boucle */
				move->xf = move->xi;
				move->yf = move->yi;
			} else if ( move->xf == 0) { /* si on peut entrer dans une boucle */
				srkrt_loop_arrived(move, sens);
				(*loopNumber)++;
			} else { /* sinon on se déplace d'une case vers le haut */
				(move->xf)--;
			}
			break;
		case DOWN:
			/* si on se trouve dans un angle : x = 5 */
			if ( (move->yf == 0) || (move->yf == 5) ) {
				/* on positionne xf et yf à xi et yi pour sortir de la boucle */
				move->xf = move->xi;
				move->yf = move->yi;
			} else if ( move->xf == 5) { /* si on peut entrer dans une boucle */
				srkrt_loop_arrived(move, sens);
				(*loopNumber)++;
			} else { /* sinon on se déplace d'une case vers le bas */
				(move->xf)++;
			}
			break;
		default:
			fprintf(stderr, "Erreur dans one_square_move : unmatched value\n");
			break;
	}
}

bool srkrt_is_allowed(Surakarta* game, Move action) {

	bool present;	
	List l ;

	l=srkrt_list_create() ;
	srkrt_allowed_moves(game, &l) ;
	/* on applique la fonction p_is_present à la liste */
	present = srkrt_list_map(&p_is_present, &l, action);
	srkrt_list_destroy(&l);
	return(present);
}

int srkrt_save(Surakarta* game, char* path) {
	/* ON A JUSTE BESOIN DE SAUVEGARDER L'HISTORIQUE */
	FILE* save;
	Stack s;
	Move m;
	
	save = fopen(path, "wt");
	if ( save == NULL ) {
		fprintf(stderr, "Erreur in srkrt_save : unable to open %s file", path);
		return(EXIT_FAILURE);
	} else {
		/* on sauvegarde le type de partie et la difficulte */
		fprintf(save, "%d %d\n", game->vs, game->level);

		/* on cree une copy de history inversee */
		s = stack_create_copy_revert(&(game->history));

		/* ecriture de l'historique */
		while ( !stack_is_empty(&s) ) {
			/* on recupere la tete*/
			m = stack_head(&s);
			/* on depile s */
			stack_remove(&s);
			/* on ecrit le Move dans le fichier */
			fprintf(save, "%d %d %d %d %d\n", m.xi, m.yi, m.xf, m.yf, m.capture);
		}
	}
	fclose(save);
	return(EXIT_SUCCESS);
}

int srkrt_load(Surakarta* game, char* path) {

	FILE* save;
	Move m;
	int turn;
	int vs, level;
	
	save = fopen(path, "rt");
	if ( save == NULL ) {
		fprintf(stderr, "Erreur in srkrt_save : unable to open %s file", path);
		return(EXIT_FAILURE);
	} else {
		/* on lit le type de partie et le niveau de difficulte */
		fscanf(save, "%d %d\n", &vs, &level);
		
		#ifndef NETWORK
		if ( vs == ON_LINE ) {
			fprintf(stderr, "Vous essayez de charger une partie en reseaux ! Utilisez la version graphique pour cela !\n");
			exit(EXIT_FAILURE);
		}
		#endif
		/* on detruit l'historique */
		stack_destroy(&(game->history));
		
		/* on deduit du premier coup le joueur qui a commence la partie */
		if ( fscanf(save, "%d %d %d %d %d\n", &(m.xi), &(m.yi), &(m.xf), &(m.yf), (int*)&(m.capture)) == EOF ) {
			fprintf(stderr, "Le fichier ne contient rien\n");
			/* on initialise surakarta */
			*game = srkrt_create(PLAYER_1);
			
		} else {
			fprintf(stderr, "%d %d %d %d", m.xi, m.yi, m.xf, m.yf);
			
			if ( m.xi < 2 ) {
				turn = PLAYER_2;
				fprintf(stderr, "Le joueur 2 a commence la partie");
			} else {
				turn = PLAYER_1;
				fprintf(stderr, "Le joueur 1 a commence la partie");
			}
			/* on initialise surakarta */
			*game = srkrt_create(turn);
			
			stack_add(&(game->history), m);
		}
		game->vs = vs;
		game->level = level;
		
		/* lecture de l'historique */
		while ( !feof(save) ) {
			/* on recupere le move*/
			fscanf(save, "%d %d %d %d %d\n", &(m.xi), &(m.yi), &(m.xf), &(m.yf), (int*)&(m.capture));
			fprintf(stderr, "%d %d %d %d\n", m.xi, m.yi, m.xf, m.yf);
			/*on l'empile*/
			stack_add(&(game->history), m);
		}
	}
	fclose(save);
	return(EXIT_SUCCESS);
}

